#pragma once
#include <iostream>
#include <string>
#include <cstdio>
#include <set>
#include "Log.hpp"
using namespace std;
const string sep = " ";
class Command
{
private:
    // 将配置文件中的信息插入到_safe_cmd中存储
    void LoadConf(const string &conf)
    {
        ifstream in(conf);
        if (in.is_open() == false)
        {
            cout << "打开配置文件" << conf << "失败" << endl;
            return;
        }
        string line;
        while (getline(in, line))
        {
            _safe_cmd.insert(line);
            cout << "加载命令" << line << "成功" << endl;
        }
        in.close();
    }
    // 提取指令前缀
    string PrefixCommand(const string &cmd)
    {
        // 如果是空串，就直接返回string即可
        if (cmd.empty() == true)
        {
            return string();
        }
        int pos = cmd.find(sep); // 查找' '位置
        if (pos == string::npos) // 没找到
        {
            return cmd;
        }
        else
        {
            return cmd.substr(0, pos); // 返回cmd字符串的左闭右开区间
        }
    }

    // 检测命令是否是安全的
    bool SafeCheck(const string &cmd)
    {
        string prefix = PrefixCommand(cmd); // 提取指令前缀
        auto iter = _safe_cmd.find(prefix); // 用前缀查找
        if (iter == _safe_cmd.end())
        {
            return false;
        }
        return true;
    }

public:
    Command(const string &cond_path)
        : _cond_path(cond_path)
    {
        LoadConf(cond_path);
    }
    // ssh远程命令服务
    string Excute(const string &cmd)
    {
        // 大致逻辑
        // 创建子进程，让子进程去执行cmd命令，再创建管道，利用dup函数将子进程的标准输出重定向到管道中，
        // 这样，子进程的执行结果就会书写到管道中，父进程从管道中获取数据即可，子进程退出，父进程等待即可
        string result = "该指令执行完毕\n";
        if (SafeCheck(cmd) == true)
        {
            // cmd指令可信
            FILE *fp = popen(cmd.c_str(), "r"); // 创建子进程和管道，子进程执行命令将结果写入到管道中，用户通过fp文件指针获取内容
            if (fp == nullptr)
            {
                return "执行" + cmd + "命令失败\n";
            }
            // 通过fp文件指针获取信息
            char buffer[1024];
            // 每次读取一行，读取到结尾会返回NULL
            while (fgets(buffer, sizeof(buffer), fp) != NULL)
            {
                result += buffer;
            }
            pclose(fp); // 关闭文件
        }
        else
        {
            return "此" + cmd + "命令不可信";
        }
        return result;
    }
    ~Command() {}

private:
    set<string> _safe_cmd; // 存储安全的命令
    string _cond_path;     // 配置文件路径
};